﻿using System;
using Nofs.Net.Common.Interfaces.Cache;
using Nofs.Net.Common.Interfaces.Library;
using Nofs.Fuse;
using Nofs.Net.Common.Interfaces.Domain;
using Nofs.Net.Domain.Impl;

namespace Nofs.Net.Fuse.Impl
{

    public class StatHandler : IStatHandler
    {
        private PathTranslator _lookup;
        private LockManager _lock;
        private LogManager _logger;
        private IFileCacheManager _cacheManager;
        private IStatMapper _statMapper;

        public StatHandler(IFileCacheManager cacheManager, PathTranslator lookup, LockManager lockManager, IStatMapper statMapper, LogManager logger)
        {
            _cacheManager = cacheManager;
            _lookup = lookup;
            _lock = lockManager;
            _logger = logger;
            _statMapper = statMapper;
        }

        //@Override
        public int chmod(String path, int mode) //throws FuseException 
        {
            int stat = 0;
            _logger.LogInfo("getattr(" + path + ")");
            try
            {
                _lock.Lock();
                IFileObject target = _lookup.TranslatePath(path);

                if (target == null)
                {
                    stat = FuseErrno.ENOENT;
                }
                else
                {
                    target.GetStat().setMode(mode);
                    SaveStat(target.GetStat());
                }
            }
            catch (System.Exception e)
            {
                throw new FuseException("chmod(" + path + "," + mode + ")", e);
            }
            finally
            {
                _lock.UnLock();
            }
            return stat;
        }

        //@Override
        public int chown(String path, int uid, int gid) //throws FuseException 
        {
            int stat = 0;
            _logger.LogInfo("getattr(" + path + ")");
            try
            {
                _lock.Lock();
                IFileObject target = _lookup.TranslatePath(path);
                if (target == null)
                {
                    stat = FuseErrno.ENOENT;
                }
                else
                {
                    target.GetStat().setUID(uid);
                    target.GetStat().setGID(gid);
                    SaveStat(target.GetStat());
                }
            }
            catch (Exception e)
            {
                throw new FuseException("chown(" + path + "," + uid + "," + gid + ")", e);
            }
            finally
            {
                _lock.UnLock();
            }
            return stat;
        }

        public static int GetMode(IFileObject file) //throws Exception 
        {
            int mode = 0;
            if (file.GetGenerationType() == GenerationType.DATA_FILE ||
               file.GetGenerationType() == GenerationType.EXECUTABLE)
            {
                mode = file.GetStat().getMode() | FuseFtypeConstants.TYPE_FILE;
            }
            else if (file.GetGenerationType() == GenerationType.DOMAIN_FOLDER)
            {
                mode = file.GetStat().getMode() | FuseFtypeConstants.TYPE_DIR;
            }
            else
            {
                throw new Exception("unknown generation type");
            }
            if (file.GetGenerationType() == GenerationType.EXECUTABLE || !file.CanWrite())
            {
                mode = mode & (~(FuseMode.S_IWUSR | FuseMode.S_IWOTH | FuseMode.S_IWGRP));
            }
            return mode;
        }

        private int GetFileSize(IFileObject target) //throws Exception 
        {
            if (target.GetGenerationType() == GenerationType.DOMAIN_FOLDER)
            {
                return 0;
            }
            else
            {
                IFileCache cachedFile = _cacheManager.GetFileCache(target);
                int size = cachedFile.GetFileSize();
                _cacheManager.DeallocateIfNotDirty(cachedFile);
                return size;
            }
        }

        //@Override
        public int getattr(String path, FuseGetattrSetter attr) //throws FuseException 
        {
            int stat = 0;
            _logger.LogInfo("getattr(" + path + ")");
            try
            {
                _lock.Lock();
                IFileObject target = _lookup.TranslatePath(path);
                if (target == null)
                {
                    stat = FuseErrno.ENOENT;
                }
                else
                {
                    attr.set(
                            0, //inode
                            GetMode(target),
                            target.GetStat().getNLink(),
                            target.GetStat().getUID(),
                            target.GetStat().getGID(),
                            target.GetStat().getRDev(),
                        //target.GetStat().getFileSize(),
                            GetFileSize(target),
                            target.GetStat().getBlocks(),
                            target.GetStat().getATime(),
                            target.GetStat().getMTime(),
                            target.GetStat().getCTime());
                }
            }
            catch (Exception e)
            {
                throw new FuseException("getattr(" + path + ")", e);
            }
            finally
            {
                _lock.UnLock();
            }
            return stat;
        }

        private void SaveStat(IFileObjectStat stat) //throws Exception 
        {
            if (stat is CustomStat)
            {
                _statMapper.Save(((CustomStat)stat).Inner());
            }
            else
            {
                _statMapper.Save((FileObjectStat)stat);
            }
        }

        //@Override
        public int utime(String path, int atime, int mtime) //throws FuseException 
        {
            int stat = 0;
            _logger.LogInfo("getattr(" + path + ")");
            try
            {
                _lock.Lock();
                IFileObject target = _lookup.TranslatePath(path);
                if (target == null)
                {
                    stat = FuseErrno.ENOENT;
                }
                else
                {
                    target.GetStat().setATime(atime);
                    target.GetStat().setMTime(mtime);
                    SaveStat(target.GetStat());
                }
            }
            catch (System.Exception e)
            {
                throw new FuseException("utime(" + path + "," + atime + "," + mtime + ")", e);
            }
            finally
            {
                _lock.UnLock();
            }
            return stat;
        }

    }

}
